package com.oracle.coherence.patterns.security;

import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofSerializer;
import com.tangosol.io.pof.PofWriter;

import javax.security.auth.Subject;
import java.io.IOException;
import java.security.Principal;
import java.util.Set;
import java.util.TreeSet;

/**
 * An implementation of a PofSerializer that will serialize and deserialize a
 * {@link javax.security.auth.Subject} that contains a {@link KrbTicketPrincipal}.
 * <p/>
 * Only the {@link KrbTicketPrincipal} is actually serialized; on deserialization a
 * new {@link javax.security.auth.Subject} will be created to contain the {@link KrbTicketPrincipal}.
 * <p/>
 * This implementation is a special case to cope with the fact that if a .Net client uses a custom IPrincipal
 * as its security credentials then the custom class is not supported directly by Coherence POF serialization.
 * As such when the credentials are deserialized on the server there will be a classcast exception as the
 * server expects to deserialize a javax.security.auth.Subject but it will actually get the Java equivalent
 * of the .Net custom IPrincipal.
 * <p/>
 *
 * @author Jonthan Knight
 */
public class DotNetPrincipalDeserializer implements PofSerializer {

    /**
     * Serialize a user type instance to a POF stream by writing its state using the specified
     * {@link com.tangosol.io.pof.PofWriter} object.<p/>
     * If the object to serialize is a {@link KrbTicketPrincipal} then it is serialized directly to the
     * {@link com.tangosol.io.pof.PofWriter}. If the object to serialize is a {@link javax.security.auth.Subject}
     * then the {@link KrbTicketPrincipal} is extracted and serialized.
     * <p/>
     *
     * @param pofWriter - the {@link com.tangosol.io.pof.PofWriter} with which to write the object's state
     * @param o         - the object to serialize
     * @throws IOException - if an I/O error occurs
     */
    public void serialize(PofWriter pofWriter, Object o) throws IOException {
        KrbTicketPrincipal principal = null;

        if (o instanceof Subject) {
            Set<KrbTicketPrincipal> set = ((Subject) o).getPrincipals(KrbTicketPrincipal.class);
            if (!set.isEmpty()) {
                principal = set.iterator().next();
            }
        } else if (o instanceof KrbTicketPrincipal) {
            principal = (KrbTicketPrincipal) o;
        }

        if (principal != null) {
            principal.writeExternal(pofWriter);
        }
    }

    /**
     * Deserialise a {@link KrbTicketPrincipal} from the specified {@link com.tangosol.io.pof.PofReader}
     * and return it wrapped in a {@link javax.security.auth.Subject}.
     * <p/>
     *
     * @param pofReader - the PofReader with which to read the object's state
     * @return A {@link javax.security.auth.Subject} containing a {@link KrbTicketPrincipal} deserialized from
     *         the {@link com.tangosol.io.pof.PofReader}.
     * @throws IOException - If an I/O error occurs.
     */
    public Object deserialize(PofReader pofReader) throws IOException {
        KrbTicketPrincipal principal = new KrbTicketPrincipal();
        principal.readExternal(pofReader);
        Set<Principal> principals = new TreeSet<Principal>();
        principals.add(principal);

        Set<?> publicCreds = new TreeSet();
        Set<?> privateCreds = new TreeSet();

        return new Subject(true, principals, publicCreds, privateCreds);
    }
}
